import torch
import torch.nn as nn
import torchvision.models as models

import Utils
from Modules.SPPModule import SPPModule
from Models.ModelRegister import DefaultModelRegister as Register
from Modules.DCA import DCA, get_dark_channel
from Modules.CrossAttention import CrossAttention
from Modules.SeModule import SeModule
import torch.nn.functional as F
from  torch.nn import TransformerEncoderLayer
from Utils import sobel_conv

@Register.register("DCFNet")
class DCFNet(nn.Module):
    def __init__(self, num_classes=1,  sizes=[(2,2)], pool_type="avg"):
        super().__init__()
        self.resnet = models.resnet34(pretrained=True)
        numFit = self.resnet.fc.in_features
        self.resnet.avgpool = SPPModule(sizes, pool_type)
        size2_sum = 0
        for i, j in sizes:
            size2_sum += i * j
        self.resnet.fc = nn.Linear(numFit * size2_sum, numFit)
        self.act = nn.LeakyReLU()
        self.fc2 = nn.Linear(numFit, num_classes)
        self.dc_resnet = models.resnet34(pretrained=True)
        self.dc_resnet.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)

        self.fusion1 = CrossAttention(256, 256, 256)
        self.fusion2 = CrossAttention(512, 512, 512)

        self.dca = DCA(3)
        self.se = SeModule(512)

    def forward(self, x):
        dark = get_dark_channel(x)
        x = self.dca(x)
        x = self.resnet.conv1(x)
        x = self.resnet.bn1(x)
        x = self.resnet.relu(x)
        x = self.resnet.maxpool(x)
        x = self.resnet.layer1(x)
        x = self.resnet.layer2(x)
        x = self.resnet.layer3(x)


        dark = self.dc_resnet.conv1(dark)
        dark = self.dc_resnet.bn1(dark)
        dark = self.dc_resnet.relu(dark)
        dark = self.dc_resnet.maxpool(dark)
        dark = self.dc_resnet.layer1(dark)
        dark = self.dc_resnet.layer2(dark)
        dark = self.dc_resnet.layer3(dark)

        x,dark = self.fusion1([x,dark])

        x = self.resnet.layer4(x)
        dark = self.dc_resnet.layer4(dark)
        x, dark = self.fusion2([x, dark])

        x = self.se(x)
        x = self.resnet.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.resnet.fc(x)
        x = self.fc2(self.act(x))
        return x


if __name__ == '__main__':
    x = torch.rand((3,3,224,224))
    model = DCFNet(1)
    print(model)
    print(model(x))
